'''
Created on Jan 20, 2010

@author: madsk
'''

from PyQt4 import QtCore, QtGui
from common.overlays import *
from common import GraphicsScene
from manipulatedimage import ManipulatedImage
from savedialog import SaveDialog
from sliderdialog import SliderDialog
from scaledialog import ScaleDialog
from processingoverlay import ProcessingOverlay
import os

class GraphicsView(QtGui.QGraphicsView):
    # Signals.
    toggleFullScreen = QtCore.pyqtSignal()
    modeChange = QtCore.pyqtSignal(str)
    applyImageOperations = QtCore.pyqtSignal(str, list)
    
    FULLSCREEN = QtCore.QRectF(0, 0, 800, 480)
    WINDOWED = QtCore.QRectF(0, 0, 800, 424)
    
    def __init__(self, parent=None):
        super(QtGui.QGraphicsView, self).__init__(parent)
        self._fullscreen = None
        self._temporaryOverlays = []
    
        # Build a scene.
        self._scene = GraphicsScene(self)
        self._image = None
        self._pixmapitem = None
        self.setScene(self._scene)
        self._scene.clicked.connect(lambda: self._showOverlay('scene-clicked'))
        
        # Remove scrollbars.
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        # Add the overlay buttons.
        self._fullscreenbutton = FullScreenButton()
        self._scene.addItem(self._fullscreenbutton)
        self._fullscreenbutton.setZValue(1)
        self._fullscreenbutton.clicked.connect(self._handleFullScreenClicked)
        self._fullscreenbutton.setVisible(False)

        self._exitbutton = ExitButton()
        self._scene.addItem(self._exitbutton)
        self._exitbutton.setZValue(1)
        self._exitbutton.clicked.connect(self._handleExitClicked)
        self._exitbutton.setVisible(False)
        
        # Sharpen, blur
        self._sharpenblurbutton = SharpenBlurButton()
        self._scene.addItem(self._sharpenblurbutton)
        self._sharpenblurbutton.setZValue(1)
        self._sharpenblurbutton.clicked.connect(lambda: self._showOverlay('sharpenblur'))
        self._sharpenblurbutton.setVisible(True)

        self._sharpenbutton = SharpenButton()
        self._scene.addItem(self._sharpenbutton)
        self._sharpenbutton.setZValue(1)
        self._sharpenbutton.clicked.connect(lambda: self._applyImageOperation('sharpen'))
        self._sharpenbutton.setVisible(False)

        self._blurbutton = BlurButton()
        self._scene.addItem(self._blurbutton)
        self._blurbutton.setZValue(1)
        self._blurbutton.clicked.connect(lambda: self._applyImageOperation('blur'))
        self._blurbutton.setVisible(False)        

        # Color, contrast, brightness
        self._colorcontrastbrightnessbutton = ColorContrastBrightnessButton()
        self._scene.addItem(self._colorcontrastbrightnessbutton)
        self._colorcontrastbrightnessbutton.setZValue(1)
        self._colorcontrastbrightnessbutton.clicked.connect(lambda: self._showOverlay('colorcontrastbrightness'))
        self._colorcontrastbrightnessbutton.setVisible(True)

        self._colorbutton = ColorButton()
        self._scene.addItem(self._colorbutton)
        self._colorbutton.setZValue(1)
        self._colorbutton.clicked.connect(lambda: self._applyImageOperation('color'))
        self._colorbutton.setVisible(False)        

        self._contrastbutton = ContrastButton()
        self._scene.addItem(self._contrastbutton)
        self._contrastbutton.setZValue(1)
        self._contrastbutton.clicked.connect(lambda: self._applyImageOperation('contrast'))
        self._contrastbutton.setVisible(False)        

        self._brightnessbutton = BrightnessButton()
        self._scene.addItem(self._brightnessbutton)
        self._brightnessbutton.setZValue(1)
        self._brightnessbutton.clicked.connect(lambda: self._applyImageOperation('brightness'))
        self._brightnessbutton.setVisible(False)        

        # Fliph, flipv, scale
        self._flipscalebutton = FlipScaleButton()
        self._scene.addItem(self._flipscalebutton)
        self._flipscalebutton.setZValue(1)
        self._flipscalebutton.clicked.connect(lambda: self._showOverlay('flipscale'))
        self._flipscalebutton.setVisible(True)

        self._fliphbutton = FlipHorizontalButton()
        self._scene.addItem(self._fliphbutton)
        self._fliphbutton.setZValue(1)
        self._fliphbutton.clicked.connect(lambda: self._applyImageOperation('fliphorizontal'))
        self._fliphbutton.setVisible(False)        

        self._flipvbutton = FlipVerticalButton()
        self._scene.addItem(self._flipvbutton)
        self._flipvbutton.setZValue(1)
        self._flipvbutton.clicked.connect(lambda: self._applyImageOperation('flipvertical'))
        self._flipvbutton.setVisible(False)        

        self._scalebutton = ScaleButton()
        self._scene.addItem(self._scalebutton)
        self._scalebutton.setZValue(1)
        self._scalebutton.clicked.connect(lambda: self._applyImageOperation('scale'))
        self._scalebutton.setVisible(False)        
        
        # Undo
        self._undobutton = UndoButton()
        self._scene.addItem(self._undobutton)
        self._undobutton.setZValue(1)
        self._undobutton.clicked.connect(self._undoImageOperation)
        self._undobutton.setVisible(True)

        # Create an overlay that can be used to show that the image is being processed.
        self._processingbox = ProcessingOverlay()
        self._scene.addItem(self._processingbox)
        self._processingbox.setVisible(False)

        # Register a timer for removing the temporary buttons after two seconds.
        self._timer = QtCore.QTimer()
        self._timer.setSingleShot(True)
        self._timer.setInterval(2000)
        self._timer.timeout.connect(self._hideTemporaryButtons)
                    
    def setFullScreen(self, fullscreen):
        self._fullscreen = fullscreen
        if fullscreen == True:
            self._scene.setSceneRect(GraphicsView.FULLSCREEN)
            self._fullscreenbutton.setPos(690, 370)
            self._exitbutton.setPos(350, 370)

            # Sharpen, blur
            self._sharpenblurbutton.setPos(10, 16)
            self._sharpenbutton.setPos(120, 16)
            self._blurbutton.setPos(230, 16)
            
            # Color, contrast, brightness
            self._colorcontrastbrightnessbutton.setPos(10, 132)
            self._colorbutton.setPos(120, 132)
            self._contrastbutton.setPos(230, 132)
            self._brightnessbutton.setPos(340, 132)
            
            # Fliph, flipv, scale
            self._flipscalebutton.setPos(10, 248)
            self._fliphbutton.setPos(120, 248)
            self._flipvbutton.setPos(230, 248)
            self._scalebutton.setPos(340, 248)
            
            # Undo
            self._undobutton.setPos(10, 364)
        else:
            self._scene.setSceneRect(GraphicsView.WINDOWED)
            self._fullscreenbutton.setPos(690, 314)
            self._exitbutton.setPos(350, 314)
            
            # Sharpen, blur
            self._sharpenblurbutton.setPos(10, 5)
            self._sharpenbutton.setPos(120, 5)
            self._blurbutton.setPos(230, 5)

            # Color, contrast, brightness
            self._colorcontrastbrightnessbutton.setPos(10, 110)
            self._colorbutton.setPos(120, 110)
            self._contrastbutton.setPos(230, 110)
            self._brightnessbutton.setPos(340, 110)

            # Fliph, flipv, scale
            self._flipscalebutton.setPos(10, 215)
            self._fliphbutton.setPos(120, 215)
            self._flipvbutton.setPos(230, 215)
            self._scalebutton.setPos(340, 215)

            # Undo
            self._undobutton.setPos(10, 320)

        self._positionImage()
            
    def setImage(self, image):
        # Store the image.
        self._image = ManipulatedImage(image)
        self._image.finished.connect(self._conditionalUpdateImage)
        self._updateImage()
        
    def _updateImage(self):
        # Remove the old pixmap image.
        if self._pixmapitem:
            self._scene.removeItem(self._pixmapitem)
            self._pixmapitem = None
            
        # Add and position the new one.
        self._pixmapitem = self._scene.addPixmap(self._image.pixmap)
        self._positionImage()
        
    def _conditionalUpdateImage(self, update):
        # This update operation is called when the image worker thread
        # is done processing the preview image.
        if update:
            self._updateImage()
        self._processingbox.setVisible(False)

    def _showOverlay(self, group):
        self._timer.stop()
        self._hideTemporaryButtons()
        if group == 'scene-clicked':
            self._displayTemporaryButtons(self._exitbutton, self._fullscreenbutton)
        elif group == 'sharpenblur':
            self._displayTemporaryButtons(self._sharpenbutton, self._blurbutton)
        elif group == 'colorcontrastbrightness':
            self._displayTemporaryButtons(self._colorbutton, self._contrastbutton, self._brightnessbutton)
        elif group == 'flipscale':
            self._displayTemporaryButtons(self._fliphbutton, self._flipvbutton, self._scalebutton)
        self._timer.start()
            
    def _displayTemporaryButtons(self, *buttons):
        for button in buttons:
            button.setVisible(True)
            self._temporaryOverlays.append(button)
    
    def _hideTemporaryButtons(self):
        while len(self._temporaryOverlays) > 0:
            button = self._temporaryOverlays.pop()
            button.setVisible(False)

    def _positionImage(self):
        if self._pixmapitem:
            imagesize = self._pixmapitem.boundingRect()
            canvassize = GraphicsView.FULLSCREEN if self._fullscreen else GraphicsView.WINDOWED
            self._pixmapitem.setPos((canvassize.width() - imagesize.width()) / 2,
                                    (canvassize.height() - imagesize.height()) / 2)
                
    def _handleFullScreenClicked(self):
        self._timer.stop()
        self._hideTemporaryButtons()
        self.toggleFullScreen.emit()
        
    def _handleExitClicked(self):
        # Remove temporary buttons.
        self._timer.stop()
        self._hideTemporaryButtons()
        
        # Check whether the image has been modified.
        if self._image.isModified():
            savedialog = SaveDialog(self)
            choice = savedialog.exec_()
            if choice == 0: # Cancel
                # Go back to editor mode.
                pass
            elif choice == 1: # Ok
                self.applyImageOperations.emit(self._image.path, self._image.operations())
                self.modeChange.emit('Browser')
            else: # Discard
                self._image.reset()
                self.modeChange.emit('Browser')
        else:
           # The image is not modified.
           self.modeChange.emit('Browser') 
        
    def _applyImageOperation(self, operation, args = ()):
        self._timer.stop()
        self._hideTemporaryButtons()
        
        # Check to see if the operation needs a slider.
        if operation in ('color', 'contrast', 'brightness'):
            sliderdialog = SliderDialog("Select the desired %s adjustment."%operation)
            choice = sliderdialog.exec_()
            if choice == 0:
                # Cancel - go back to the editor.
                return
            else:
                # Ok - perform the operation.
                args = ( sliderdialog.getSliderValue(), )

        # Handle the scale operation.
        elif operation == 'scale':
            scaledialog = ScaleDialog()
            choice = scaledialog.exec_()
            if choice == 0:
                # Cancel - go back to the editor.
                return
            else:
                # Ok - perform the operation.
                args = ( scaledialog.getResolution(), )

        
        # Start the worker thread.
        self._processingbox.setVisible(True)
        self._image.performOperation(operation, args)
        
    def _undoImageOperation(self):
        try:
            self._image.undoOperation()
            self._updateImage()
        except Exception, e:
            # No undo information is available. Just silently ignore that for now.
            pass
